/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Copyright 2002-2003 by Mark Wolgemuth, All rights reserved.             */
/* Copyright 2002-2003 by Employease, Inc.  All rights reserved.           */
/*                                                                         */
/* Redistribution and use in source and binary forms, with or without      */
/* modification, are permitted provided that the following conditions      */
/* are met:                                                                */
/*                                                                         */
/*    1. Redistributions of source code must retain the above copyright    */
/* notice, this list of conditions and the following disclaimer.           */
/*    2. Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in the     */
/* documentation and/or other materials provided with the distribution.    */
/*                                                                         */
/* THIS SOFTWARE IS PROVIDED BY THE MOD_ATHENA PROJECT ``AS IS'' AND ANY   */
/* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE       */
/* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR      */
/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR        */
/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,   */
/* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,     */
/* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR      */
/* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT            */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   */
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "ath.h"

/* handle update expect logic
 * mark down (allow_ttl) time is how long we wait for update
 * before marking down
 * mark up (recover_ttl) marks up after the most recent update
 * time exceeds specified seconds after mark down time
 */
void 
ath_algo_expect_update(ath_farm *farm, ath_phys *phys)
{
    apr_time_t timenow = apr_time_now();
    apr_time_t l_attl;
    apr_time_t l_rttl;
    ath_lock *lock;
    ASSERT(farm);
    ASSERT(phys);

    lock = ath_lock_for_idx(phys->lock_idx);
    l_attl = farm->allow_ttl * 1000000; /* secs to msecs */
    l_rttl = farm->recover_ttl * 1000000; /* secs to msecs */
    /* mark down if TTL since update has expired */
    if (timenow - phys->mtime > l_attl)
      {
	  ath_lock_acquire(lock);
	  phys->in = ATH_DOWN;
	  phys->dtime = timenow;
	  ath_lock_release(lock);
      }
    /* only bring back up if recover_ttl is defined > 0 */
    else if ((l_rttl != 0) && (phys->mtime - phys->dtime > l_rttl))
      {
	  ath_lock_acquire(lock);
	  phys->in = ATH_UP;
	  ath_lock_release(lock);
      }
}

/* hit adds logic 
 * every request automatically adds specefied value
 * to specified field
 * will be reset when next update comes in for that field
 * use this to offset delays in updates when hit is known
 * to cause load
 */
void 
ath_algo_hit_adds(ath_farm *farm, ath_phys *phys)
{
    ath_lock *lock;

    /* sorry but easier to see at beginning */
    if ((farm == NULL) || (phys == NULL))
	return;
    if (farm->hit_adds_to == (int) ATH_ALGO_NON)
	return;
    /* handle auto-loading */
    lock = ath_lock_for_idx(phys->lock_idx);
    ath_lock_acquire(lock);
    switch (farm->hit_adds_to)
      {
      case ATH_ALGO_CPU:
	  /* overflow? conceivable. don't care. */
	  phys->cpu = phys->cpu + farm->hit_adds;
	  break;
      case ATH_ALGO_NET:
	  phys->net = phys->net + farm->hit_adds;
	  break;
      case ATH_ALGO_LOD:
	  phys->ld = phys->ld + farm->hit_adds;
	  break;
      case ATH_ALGO_MEM:
	  phys->mem = phys->mem + farm->hit_adds;
	  break;
      case ATH_ALGO_DIS:
	  phys->disk = phys->disk + farm->hit_adds;
	  break;
      case ATH_ALGO_CU0:
	  phys->cus0 = phys->cus0 + farm->hit_adds;
	  break;
      case ATH_ALGO_CU1:
	  phys->cus1 = phys->cus1 + farm->hit_adds;
	  break;
      case ATH_ALGO_CU2:
	  phys->cus2 = phys->cus2 + farm->hit_adds;
	  break;
      case ATH_ALGO_CU3:
	  phys->cus3 = phys->cus3 + farm->hit_adds;
	  break;
      case ATH_ALGO_CU4:
	  phys->cus4 = phys->cus4 + farm->hit_adds;
	  break;
      case ATH_ALGO_CU5:
	  phys->cus5 = phys->cus5 + farm->hit_adds;
	  break;
      case ATH_ALGO_CU6:
	  phys->cus6 = phys->cus6 + farm->hit_adds;
	  break;
      case ATH_ALGO_CU7:
	  phys->cus7 = phys->cus7 + farm->hit_adds;
	  break;
      case ATH_ALGO_CU8:
	  phys->cus8 = phys->cus8 + farm->hit_adds;
	  break;
      case ATH_ALGO_CU9:
	  phys->cus9 = phys->cus9 + farm->hit_adds;
	  break;
      }
    ath_lock_release(lock);
}

int 
ath_algo_expect_get_chosen(apr_pool_t *pool, 
			   ath_farm *farm, ath_algo_def *algo_def)
{
    int chosen_idx = ATH_EOA;
    ath_phys *phys = NULL;
    ASSERT(farm);

    /* if expecting update, check table for changes
       this used to be done inside algorithms to reduce looping 
       but cost is so low i moved here to make algorithm.c read 
       only on data.
       we need to update them all so all are available to algo.
    */
    if (farm->expect_update == ATH_ON)
      {
	  int idx, i;
	  for (i = 0; farm->member_phys_idx[i] != ATH_EOA; i++)
	    {
		/* skip if it was freed using update farm remove */
		if (farm->member_phys_idx[i] != ATH_OPN)
		  {
		      idx = farm->member_phys_idx[i];
		      phys = ath_phys_for_idx(idx);
		      ath_algo_expect_update(farm, phys);
		  }
	    }
      }
    
    /* a server could get timeout in this window, but who cares?
       in the context of network latencies there are worse things
       possible
    */
    if (((algo_def) && (algo_def->round_robin == ATH_ON))
	|| (farm->algo_def->round_robin == ATH_ON))
      {
	  ath_lock *lock = ath_lock_for_idx(farm->lock_idx);
	  int rrctx = farm->member_chosen_idx;
	  chosen_idx = ath_algo_get_chosen(pool, farm, algo_def, &rrctx);
	  DEBUGERR("Got chosen idx[%d]", chosen_idx);
	  ath_lock_acquire(lock);
	  farm->member_chosen_idx = rrctx;
	  ath_lock_release(lock);
      }
    else
	chosen_idx = ath_algo_get_chosen(pool, farm, algo_def, NULL);
    return chosen_idx;
}
